//
//  SSafe.m
//  安全类
//
//  Created by BrightLi on 2016/12/22.
//  Copyright © 2021年 TJZ. All rights reserved.
//

#import "YMSafeUtil.h"

#import "NSObject+Safe.h"
#import "NSString+Safe.h"
#import "NSArray+Safe.h"
#import "NSMutableArray+Safe.h"
#import "NSDictionary+Safe.h"
#import "NSSet+Safe.h"
#import "YMSafeTest.h"

#import <objc/runtime.h>

@implementation MyAssertHandler

//处理OC的断言
- (void) handleFailureInMethod:(SEL)selector object:(id)object file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format,...
{
    NSString *info=[NSString stringWithFormat:@"NSAssert-OC断言:方法 %@ 对象 %@ #%@#%li", NSStringFromSelector(selector), object, fileName, (long)line];
    [YMSafeUtil log:@"%@",info];
}
//处理C的断言
- (void) handleFailureInFunction:(NSString *)functionName file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format,...
{
    NSString *info=[NSString stringWithFormat:@"NSCAssert-C断言:方法 %@ %@#%li", functionName, fileName, (long)line];
    [YMSafeUtil log:@"%@",info];
}

@end

@implementation YMSafeUtil
// 交接类方法
+ (void) swizzleClass:(Class) klass
                 sel1:(SEL)sel1
                 sel2:(SEL)sel2
{
    Method method1 = class_getClassMethod(klass, sel1);
    Method method2 = class_getClassMethod(klass, sel2);
    method_exchangeImplementations(method1, method2);
}
// 交换实例方法
+ (void) swizzleInstance:(Class) klass
                    sel1:(SEL)sel1
                    sel2:(SEL)sel2
{
    Method originalMethod = class_getInstanceMethod(klass, sel1);
    Method swizzledMethod = class_getInstanceMethod(klass, sel2);
    BOOL didAddMethod =
    class_addMethod(klass,
                    sel1,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    if (didAddMethod){
        class_replaceMethod(klass,
                            sel2,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    }else{
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

// 修正闪退
+ (void) fixed
{
//    static dispatch_once_t onceToken;
//    dispatch_once(&onceToken, ^{
        [NSObject safe_fixed];
        [NSString safe_fixed];
        [NSAttributedString safe_fixed];
        [NSMutableAttributedString safe_fixed];
        [NSMutableString safe_fixed];
        [NSArray safe_fixed];
        [NSMutableArray safe_fixed];
        [NSDictionary safe_fixed];
        [NSMutableDictionary safe_fixed];
        [NSSet safe_fixed];
        [UITextField safe_fixed];
        [UINavigationController safe_fixed];
        [UIViewController safe_fixed];
#if DEBUG
        [UIView safe_fixed];
        [UIControl safe_fixed];
        [UIApplication safe_fixed];
#endif
        [self fixedH5];
        NSLog(@"启动数据安全");
//    });
#if DEBUG
    [YMSafeTest test];
#endif
}

// GCD单例
+ (instancetype) shared
{
    static dispatch_once_t onceToken;
    static YMSafeUtil * instance;
    dispatch_once(&onceToken, ^{
        instance=[[YMSafeUtil alloc] init];
    });
    return instance;
}

// 输出到日志
+ (void) log:(NSString *)format,... NS_FORMAT_FUNCTION(1,2)
{
    if(!format){
        return;
    }
    va_list args;
    va_start(args, format);
    NSString *info= [[NSString alloc] initWithFormat:format arguments:args];
    va_end(args);
    NSString *place = [YMSafeUtil stackPlace:3];
    if(YMSafeUtil.shared.block){
        YMSafeUtil.shared.block(@"警告",info,place);
    }
}
// 异常堆栈
+(void) stackLog:(NSException *) exception start:(int) start
{
    NSString *place = [YMSafeUtil stackPlace:start];
    NSString *name=exception.name;
    NSString *reason = exception.reason;
    if(YMSafeUtil.shared.block){
        YMSafeUtil.shared.block(name,reason,place);
    }
}
// 获取堆栈中调用位置
+ (NSString *) stackPlace:(int)start
{
    //堆栈数据
    NSArray *callStackSymbols = [NSThread callStackSymbols];
    //获取在哪个类的哪个方法中实例化的数组  字符串格式 -[类名 方法名]  或者 +[类名 方法名]
    NSString *mainCallStackSymbolMsg = [self stackInfo:callStackSymbols start:start];
    if (mainCallStackSymbolMsg.length<1){
        mainCallStackSymbolMsg = @"崩溃方法定位失败,请您查看函数调用堆栈来排查错误原因";
        return mainCallStackSymbolMsg;
    }
    NSString *place=[NSString stringWithFormat:@"%@",mainCallStackSymbolMsg];
    return place;
}
// 获取堆栈主要崩溃精简化的信息<根据正则表达式匹配出来>
+ (NSString *) stackInfo:(NSArray<NSString *> *)callStackSymbols
                   start:(int) start
{
    //mainCallStackSymbolMsg的格式为 +[类名 方法名]  或者 -[类名 方法名]
    __block NSString *mainCallStackSymbolMsg = nil;
    //匹配出来的格式为 +[类名 方法名]  或者 -[类名 方法名]
    NSString *regularExpStr = @"[-\\+]\\[.+\\]";
    NSRegularExpression *regularExp = [[NSRegularExpression alloc] initWithPattern:regularExpStr options:NSRegularExpressionCaseInsensitive error:nil];
    for (int index = start; index < callStackSymbols.count; index++) {
        NSString *callStackSymbol = callStackSymbols[index];
        [regularExp enumerateMatchesInString:callStackSymbol options:NSMatchingReportProgress range:NSMakeRange(0, callStackSymbol.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
            if (result) {
                NSString* tempCallStackSymbolMsg = [callStackSymbol substringWithRange:result.range];
                NSString *className = [tempCallStackSymbolMsg componentsSeparatedByString:@" "].firstObject;
                className = [className componentsSeparatedByString:@"["].lastObject;
                //filter category and system class
                if (![className hasSuffix:@")"]) {
                    mainCallStackSymbolMsg = tempCallStackSymbolMsg;
                }
                *stop = true;
            }
        }];
        if (mainCallStackSymbolMsg.length) {
            break;
        }
    }
    return mainCallStackSymbolMsg;
}

// H5 闪退修复
+ (void) fixedH5
{   
    // WebActionDisablingCALayerDelegate
//    NSString *name=[QLDataUtil decode3DES:@"385e3773a7a1036e21884063824b1a7a47e8eefc5e215a9517716ddf8d886541e209bb3e4eaf4aca" key:QLDataUtil.desKey];
//    Class class= NSClassFromString(name);
//    // setBeingRemoved
//    NSString *method1=[QLDataUtil decode3DES:@"5fa92109c9483d9901699b02986742bd" key:QLDataUtil.desKey];
//    // willBeRemoved
//    NSString *method2=[QLDataUtil decode3DES:@"418e5d71102b643398661e1fa1972da8" key:QLDataUtil.desKey];
//    // removeFromSuperview
//    NSString *method3=[QLDataUtil decode3DES:@"d1ad2e1e3e18786c16506ed2d11e7273266e1e4b361881d3" key:QLDataUtil.desKey];
//    SEL sel1=NSSelectorFromString(method1);
//    SEL sel2=NSSelectorFromString(method2);
//    SEL sel3=NSSelectorFromString(method3);
//    class_addMethod(class, sel1, setBeingRemoved, "v@:");
//    class_addMethod(class, sel2, willBeRemoved, "v@:");
//    class_addMethod(class, sel3, willBeRemoved, "v@:");
}

id setBeingRemoved(id self, SEL selector, ...)
{
    return nil;
}

id willBeRemoved(id self, SEL selector, ...)
{
    return nil;
}

// 更安全的主线程检查
+ (BOOL) isMainQueue
{
    static const void* mainQueueKey = @"mainQueue";
    static void* mainQueueContext = @"mainQueue";
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueContext, nil);
    });
    return dispatch_get_specific(mainQueueKey) == mainQueueContext;
}

@end
